﻿#include "H264OrH265FilePlayWindow.h"
#include <QFileDialog>
#include <QSize>
#include <QMessageBox>

H264OrH265FilePlayWindow::H264OrH265FilePlayWindow(QWidget* parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	this->setWindowTitle("H264或H265文件播放器");
	setAttribute(Qt::WA_DeleteOnClose, true);  // 关闭时自动销毁窗口
	codecId = AV_CODEC_ID_H264;
}

void H264OrH265FilePlayWindow::onVideoFormatChanged(int pos)
{
	switch (pos)
	{
	case 0:
		codecId = AV_CODEC_ID_H264;
		break;
	case 1:
		codecId = AV_CODEC_ID_HEVC;
		break;
	}
}

void H264OrH265FilePlayWindow::onCodecFormatChanged(int pos)
{
	switch (pos)
	{
	case 0:
		isHwCodec = false;
		break;
	case 1:
		isHwCodec = true;
		break;
	}
}

void H264OrH265FilePlayWindow::onSelectedFile()
{
	QString filter = "所有文件(*.*)"; //文件过滤器
	QString filePath = QFileDialog::getOpenFileName(this, "选择文件输出目录", "", filter);
	this->fileName = filePath;
	this->ui.outputPathEdit->setText(this->fileName);
}

void H264OrH265FilePlayWindow::onStartPlayVideo()
{

	isRunning = true;
	videoWidth = ui.editWidth->text().toInt();
	videoHeight = ui.editHeight->text().toInt();
	if (this->fileName.isEmpty() || videoWidth <= 0 || videoHeight <= 0) {
		QMessageBox::information(this, "温馨提示", "参数不对请检查参数");
		return;
	}
	ui.labelVideo->move(0, 0);
	ui.labelVideo->resize(QSize(videoWidth, videoHeight));
	resize(QSize(videoWidth, videoHeight));

	//创建一个线程解码
	renderView = IVideoRenderView::Create();
	//IVideoRenderView::YUV420P
	if (isHwCodec)
	{
		renderView->Init(videoWidth, videoHeight, IVideoRenderView::NV12, (void*)ui.labelVideo->winId());
	}
	else {
		renderView->Init(videoWidth, videoHeight, IVideoRenderView::YUV420P, (void*)ui.labelVideo->winId());
	}
	reader = BinaryFileReader::Create();
	reader->filename = ui.outputPathEdit->text().toStdString();//文件名
	reader->width = videoWidth;
	reader->height = videoHeight;
	reader->Init();
	renderThread = std::make_unique<std::thread>(&H264OrH265FilePlayWindow::Draw, this);

}

void H264OrH265FilePlayWindow::Draw()
{

	//找打解码器
	auto codec = avcodec_find_decoder((AVCodecID)codecId);
	if (!codec)
	{
		qDebug() << "无法找到解码器->avcodec_find_decoder";
		return;
	}
	//创建解码器上下文
	auto codecCtx = avcodec_alloc_context3(codec);
	if (!codecCtx)
	{
		qDebug() << "解码器山下文创建失败->avcodec_alloc_context3";
		return;
	}
	codecCtx->thread_count = 16;//16线程解码

	if (isHwCodec) {
		//硬件加速格式 DXVA2
		auto hw_type = AV_HWDEVICE_TYPE_DXVA2;
		/// 打印所有支持的硬件加速方式
		for (int i = 0;; i++)
		{
			auto config = avcodec_get_hw_config(codec, i);
			if (!config)break;
			if (config->device_type)
				qDebug() << av_hwdevice_get_type_name(config->device_type);
		}
		//初始化硬件加速上下文
		AVBufferRef* hw_ctx = nullptr;
		av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);
		//设定硬件GPU加速
		codecCtx->hw_device_ctx = av_buffer_ref(hw_ctx);
	}

	//打开解码器
	int ret = avcodec_open2(codecCtx, NULL, NULL);
	if (ret != 0)
	{
		qDebug() << "解码器打开失败->avcodec_open2";
		return;
	}

	//创建文件分割上下文
	auto parser = av_parser_init(codecId);
	//申请packet空间
	auto pkt = av_packet_alloc();
	//申请frame空间
	auto frame = av_frame_alloc();
	//硬件加速转换AVFrame用
	auto hw_frame = av_frame_alloc();
	while (isRunning)
	{
		bool flag = reader->Read((char*)inbuf, sizeof(inbuf));
		if (!flag)
		{
			qDebug() << "文件读取结束";
			break;
		}
		int data_size = reader->getBytesCount();
		if (data_size <= 0) {
			qDebug() << "没有读到数据退出循环";
			break;
		}
		auto data = inbuf;
		while (data_size > 0) //一次有多帧数据
		{
			//通过0001 截断输出到AVPacket 返回帧大小
			int ret = av_parser_parse2(parser, codecCtx,
				&pkt->data, &pkt->size, //输出
				data, data_size,        //输入
				AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0
			);
			data += ret;
			data_size -= ret; //已处理
			if (pkt->size)
			{
				//cout << pkt->size << " "<<flush;
				//发送packet到解码线程
				ret = avcodec_send_packet(codecCtx, pkt);
				if (ret < 0)
					break;
				//获取多帧解码数据
				while (ret >= 0)
				{
					//每次回调用av_frame_unref 
					ret = avcodec_receive_frame(codecCtx, frame);
					if (ret < 0)
						break;
					auto pframe = frame;
					if (isHwCodec)
					{
						if (codecCtx->hw_device_ctx)//如果支持硬解码
						{
							//硬解码转换GPU =》CPU 显存=》内存
							//AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0
							av_hwframe_transfer_data(hw_frame, frame, 0);
							pframe = hw_frame;
							qDebug() << "硬解码";
						}
					}
					
					qDebug() << frame->format << "";
					renderView->DrawFrame(pframe);
					std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 25));
				}
			}
		}

	}
	///取出缓存数据
	ret = avcodec_send_packet(codecCtx, NULL);
	while (ret >= 0)
	{
		ret = avcodec_receive_frame(codecCtx, frame);
		if (ret < 0)
			break;
		qDebug() << frame->format << "-";
	}

	av_parser_close(parser);
	avcodec_free_context(&codecCtx);
	av_frame_free(&frame);
	av_packet_free(&pkt);
}

void H264OrH265FilePlayWindow::Release()
{
	if (isRunning)
	{
		isRunning = false;
		renderThread->join();
		if (renderView) {
			renderView->Close();
			renderView = nullptr;
		}
		if (reader)
		{
			reader->Close();
			reader = nullptr;
		}
	}

}

void H264OrH265FilePlayWindow::closeEvent(QCloseEvent* event) {
	event->accept();  // 接受关闭事件（允许关闭）
	Release();
	qDebug() << "窗口已关闭";
}

H264OrH265FilePlayWindow::~H264OrH265FilePlayWindow()
{
}
